package keyboard

import "go-os-qemu/pkg/portio"

const (
    dataPort   = 0x60
    statusPort = 0x64
    statusOBF  = 0x01 // Output buffer full
    statusIBF  = 0x02 // Input buffer full
)

// Exported special keys (returned by PollChar when available)
const (
    KeyLeft  = rune(0xE000)
    KeyRight = rune(0xE001)
    KeyUp    = rune(0xE002)
    KeyDown  = rune(0xE003)
    KeyDelete= rune(0xE004)
    KeyHome  = rune(0xE005)
    KeyEnd   = rune(0xE006)
)

var (
    shift   bool
    e0      bool // extended prefix
    f0      bool // break prefix for set 2
    scanSet uint8 // 0=unknown, 1=set1, 2=set2
)

// Init enables keyboard scanning and drains pending buffers.
func Init() {
    // Drain any pending output
    for portio.Inb(statusPort)&statusOBF != 0 {
        _ = portio.Inb(dataPort)
    }

    // Wait until controller input buffer is clear
    for portio.Inb(statusPort)&statusIBF != 0 {
    }
    // Enable scanning: 0xF4
    portio.Outb(dataPort, 0xF4)
    // Wait for ACK (0xFA), ignore if not present
    for i := 0; i < 100000; i++ {
        if portio.Inb(statusPort)&statusOBF != 0 {
            _ = portio.Inb(dataPort)
            break
        }
    }

    // Do not force scancode set; we will auto-detect in PollChar.
}

// PollChar checks if a keypress is available; returns the ASCII rune and true.
// It ignores key releases and unsupported scan codes. Uses set 2 scancodes.
func PollChar() (rune, bool) {
    status := portio.Inb(statusPort)
    if status&statusOBF == 0 {
        return 0, false
    }
    sc := portio.Inb(dataPort)
    // Prefix handling
    if sc == 0xE0 { // extended key prefix
        e0 = true
        return 0, false
    }
    if sc == 0xF0 { // set 2 break prefix
        scanSet = 2
        f0 = true
        return 0, false
    }

    // Detect set 1 by release bit
    if sc&0x80 != 0 {
        scanSet = 1
        code := sc & 0x7F
        // set1: Shift release
        if code == 0x2A || code == 0x36 {
            shift = false
        }
        e0 = false
        return 0, false
    }

    // If unknown, heuristic decide
    if scanSet == 0 {
        if sc == 0x12 || sc == 0x59 || sc == 0x5A || sc == 0x66 {
            scanSet = 2
        } else if sc == 0x2A || sc == 0x36 || sc == 0x1C || sc == 0x0E {
            scanSet = 1
        }
    }

    switch scanSet {
    case 2:
        // extended (e0) set 2 arrows and navigation
        if e0 {
            e0 = false
            switch sc {
            case 0x6B: // Left
                return KeyLeft, true
            case 0x74: // Right
                return KeyRight, true
            case 0x75: // Up
                return KeyUp, true
            case 0x72: // Down
                return KeyDown, true
            case 0x71: // Delete
                return KeyDelete, true
            case 0x6C: // Home
                return KeyHome, true
            case 0x69: // End
                return KeyEnd, true
            }
        }
        // set2: Shift press
        if sc == 0x12 || sc == 0x59 {
            shift = true
            e0 = false
            return 0, false
        }
        // controls
        switch sc {
        case 0x5A: // Enter
            e0 = false
            return '\n', true
        case 0x66: // Backspace
            e0 = false
            return '\b', true
        case 0x0D: // Tab
            e0 = false
            return '\t', true
        }
        var ch2 rune
        if shift {
            ch2 = shiftTable2[sc]
        } else {
            ch2 = table2[sc]
        }
        e0 = false
        if ch2 == 0 {
            return 0, false
        }
        return ch2, true
    default:
        // extended (e0) set 1 arrows and navigation
        if e0 {
            e0 = false
            switch sc {
            case 0x4B: // Left
                return KeyLeft, true
            case 0x4D: // Right
                return KeyRight, true
            case 0x48: // Up
                return KeyUp, true
            case 0x50: // Down
                return KeyDown, true
            case 0x53: // Delete
                return KeyDelete, true
            case 0x47: // Home
                return KeyHome, true
            case 0x4F: // End
                return KeyEnd, true
            }
        }
        // set1: Shift press
        if sc == 0x2A || sc == 0x36 {
            shift = true
            e0 = false
            return 0, false
        }
        switch sc {
        case 0x1C: // Enter
            e0 = false
            return '\n', true
        case 0x0E: // Backspace
            e0 = false
            return '\b', true
        case 0x0F: // Tab
            e0 = false
            return '\t', true
        }
        var ch1 rune
        if shift {
            ch1 = shiftTable1[sc]
        } else {
            ch1 = table1[sc]
        }
        e0 = false
        if ch1 == 0 {
            return 0, false
        }
        return ch1, true
    }
}

// Scancode set 1 mapping tables
var table1 = [128]rune{
    /* 0x00 */ 0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, '\t',
    /* 0x10 */ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's',
    /* 0x20 */ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
    /* 0x30 */ 'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' ', 0, 0, 0, 0, 0, 0,
}

var shiftTable1 = [128]rune{
    /* 0x00 */ 0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, '\t',
    /* 0x10 */ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S',
    /* 0x20 */ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V',
    /* 0x30 */ 'B', 'N', 'M', '<', '>', '?', 0, 0, 0, ' ', 0, 0, 0, 0, 0, 0,
}

// Scancode set 2 mapping tables
var table2 = [128]rune{
    /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\t', '`', 0,
    /* 0x10 */ 0, 'q', '1', 0, 0, 0, 'z', 's', 'a', 'w', '2', 0, 'c', 'x', 'd', 'e',
    /* 0x20 */ '4', '3', ' ', 'v', 'f', 't', 'r', '5', 0, 'n', 'b', 'h', 'g', 'y', '6', 0,
    /* 0x30 */ 'm', 'j', 'u', '7', '8', 0, ',', 'k', 'i', 'o', '0', '9', '.', '/', 'l', ';',
    /* 0x40 */ 'p', '-', 0, '\'', 0, '[', '=', 0, 0, ']', 0, '\\', 0, 0, 0, 0,
}

var shiftTable2 = [128]rune{
    /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\t', '~', 0,
    /* 0x10 */ 0, 'Q', '!', 0, 0, 0, 'Z', 'S', 'A', 'W', '@', 0, 'C', 'X', 'D', 'E',
    /* 0x20 */ '$', '#', ' ', 'V', 'F', 'T', 'R', '%', 0, 'N', 'B', 'H', 'G', 'Y', '^', 0,
    /* 0x30 */ 'M', 'J', 'U', '&', '*', 0, '<', 'K', 'I', 'O', ')', '(', '>', '?', 'L', ':',
    /* 0x40 */ 'P', '_', 0, '"', 0, '{', '+', 0, 0, '}', 0, '|', 0, 0, 0, 0,
}